{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# 基于 BipartiteGraphSage 的二部图无监督学习\n",
    "\n",
    "\n",
    "二部图是电子商务推荐场景中很常见的一种图，GraphScope提供了针对二部图处理学习任务的模型。本次教程，我们将会展示GraphScope如何使用BipartiteGraphSage算法在二部图上训练一个无监督学习模型。\n",
    "\n",
    "本次教程的学习任务是链接预测，通过计算在图中用户顶点和商品顶点之间存在边的概率来预测链接。\n",
    "\n",
    "在这一任务中，我们使用GraphScope内置的BipartiteGraphSage算法在 [U2I](http://graph-learn-dataset.oss-cn-zhangjiakou.aliyuncs.com/u2i.zip) 数据集上训练一个模型，这一训练模型可以用来预测用户顶点和商品顶点之间的链接。这一任务可以被看作在一个异构链接网络上的无监督训练任务。\n",
    "\n",
    "在这一任务中，BipartiteGraphSage算法会将图中的结构信息和属性信息压缩为每个节点上的低维嵌入向量，这些嵌入和表征可以进一步用来预测节点间的链接。\n",
    "\n",
    "这一教程将会分为以下几个步骤：\n",
    "\n",
    "- 启动GraphScope的学习引擎，并将图关联到引擎上\n",
    "- 使用内置的GCN模型定义训练过程，并定义相关的超参\n",
    "- 开始训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install graphscope package if you are NOT in the Playground\n",
    "\n",
    "!pip3 install graphscope\n",
    "!pip3 uninstall -y importlib_metadata  # Address an module conflict issue on colab.google. Remove this line if you are not on colab."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import the graphscope module.\n",
    "\n",
    "import graphscope\n",
    "\n",
    "graphscope.set_option(show_log=False)  # enable logging"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load u2i dataset\n",
    "\n",
    "from graphscope.dataset import load_u2i\n",
    "\n",
    "graph = load_u2i()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Launch learning engine\n",
    "\n",
    "然后，我们需要定义一个特征列表用于图的训练。训练特征集合必须从点的属性集合中选取。在这个例子中，我们选择了 \"feature\" 属性作为训练特征集，这一特征集也是 U2I 数据中用户顶点和商品顶点的特征集。\n",
    "\n",
    "借助定义的特征列表，接下来，我们使用 [graphlearn](https://graphscope.io/docs/reference/session.html#graphscope.Session.graphlearn) 方法来开启一个学习引擎。\n",
    "\n",
    "在这个例子中，我们在 \"graphlearn\" 方法中，指定在数据中 \"u\" 类型的顶点和 \"i\" 类型顶点和 \"u-i\" 类型边上进行模型训练。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# launch a learning engine.\n",
    "\n",
    "lg = graphscope.graphlearn(\n",
    "    graph,\n",
    "    nodes=[(\"u\", [\"feature\"]), (\"i\", [\"feature\"])],\n",
    "    edges=[((\"u\", \"u-i\", \"i\"), [\"weight\"]), ((\"i\", \"u-i_reverse\", \"u\"), [\"weight\"])],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "这里我们使用内置的`BipartiteGraphSage`模型定义训练过程。你可以在 [Graph Learning Model](https://graphscope.io/docs/learning_engine.html#data-model) 获取更多内置学习模型的信息。\n",
    "\n",
    "\n",
    "在本次示例中，我们使用 tensorflow 作为神经网络后端训练器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "import graphscope.learning\n",
    "from graphscope.learning.examples import BipartiteGraphSage\n",
    "from graphscope.learning.graphlearn.python.model.tf.optimizer import get_tf_optimizer\n",
    "from graphscope.learning.graphlearn.python.model.tf.trainer import LocalTFTrainer\n",
    "\n",
    "\n",
    "# Unsupervised GraphSage.\n",
    "def train(config, graph):\n",
    "    def model_fn():\n",
    "        return BipartiteGraphSage(\n",
    "            graph,\n",
    "            config[\"batch_size\"],\n",
    "            config[\"hidden_dim\"],\n",
    "            config[\"output_dim\"],\n",
    "            config[\"hops_num\"],\n",
    "            config[\"u_neighs_num\"],\n",
    "            config[\"i_neighs_num\"],\n",
    "            u_features_num=config[\"u_features_num\"],\n",
    "            u_categorical_attrs_desc=config[\"u_categorical_attrs_desc\"],\n",
    "            i_features_num=config[\"i_features_num\"],\n",
    "            i_categorical_attrs_desc=config[\"i_categorical_attrs_desc\"],\n",
    "            neg_num=config[\"neg_num\"],\n",
    "            use_input_bn=config[\"use_input_bn\"],\n",
    "            act=config[\"act\"],\n",
    "            agg_type=config[\"agg_type\"],\n",
    "            need_dense=config[\"need_dense\"],\n",
    "            in_drop_rate=config[\"drop_out\"],\n",
    "            ps_hosts=config[\"ps_hosts\"],\n",
    "        )\n",
    "\n",
    "    graphscope.learning.reset_default_tf_graph()\n",
    "    trainer = LocalTFTrainer(\n",
    "        model_fn,\n",
    "        epoch=config[\"epoch\"],\n",
    "        optimizer=get_tf_optimizer(\n",
    "            config[\"learning_algo\"], config[\"learning_rate\"], config[\"weight_decay\"]\n",
    "        ),\n",
    "    )\n",
    "\n",
    "    trainer.train()\n",
    "    u_embs = trainer.get_node_embedding(\"u\")\n",
    "    np.save(\"u_emb\", u_embs)\n",
    "    i_embs = trainer.get_node_embedding(\"i\")\n",
    "    np.save(\"i_emb\", i_embs)\n",
    "\n",
    "\n",
    "# Define hyperparameters\n",
    "config = {\n",
    "    \"batch_size\": 128,\n",
    "    \"hidden_dim\": 128,\n",
    "    \"output_dim\": 128,\n",
    "    \"u_features_num\": 1,\n",
    "    \"u_categorical_attrs_desc\": {\"0\": [\"u_id\", 10000, 64]},\n",
    "    \"i_features_num\": 1,\n",
    "    \"i_categorical_attrs_desc\": {\"0\": [\"i_id\", 10000, 64]},\n",
    "    \"hops_num\": 1,\n",
    "    \"u_neighs_num\": [10],\n",
    "    \"i_neighs_num\": [10],\n",
    "    \"neg_num\": 10,\n",
    "    \"learning_algo\": \"adam\",\n",
    "    \"learning_rate\": 0.001,\n",
    "    \"weight_decay\": 0.0005,\n",
    "    \"epoch\": 5,\n",
    "    \"use_input_bn\": True,\n",
    "    \"act\": tf.nn.leaky_relu,\n",
    "    \"agg_type\": \"gcn\",\n",
    "    \"need_dense\": True,\n",
    "    \"drop_out\": 0.0,\n",
    "    \"ps_hosts\": None,\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 执行训练过程\n",
    "\n",
    "\n",
    "在定义完训练过程和超参后，现在我们可以使用学习引擎和定义的超参开始训练过程。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train(config, lg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
